﻿using System;
using YanZhiwei.DotNet2.Utilities.Common;

namespace YanZhiwei.DotNet2.Utilities.Base
{
    /// <summary>
    /// ObjectId 
    /// <para>参考：http://www.cnblogs.com/gaochundong/archive/2013/04/24/csharp_generate_mongodb_objectid.html </para>
    /// </summary>
    public class ObjectId
    {
        /*
         * 参考：
         * 在MongoDB中，文档（document）在集合（collection）中的存储需要一个唯一的_id字段作为
         * 主键。这个_id默认使用ObjectId来定义，因为ObjectId定义的足够短小，并尽最大可能的保持唯
         * 一性，同时能被快速的生成。
         * 
         * ObjectId 是一个 12 Bytes 的 BSON 类型，其包含：
         * 1.4 Bytes 自纪元时间开始的秒数
         * 2.3 Bytes 机器描述符
         * 3.2 Bytes 进程ID
         * 4.3 Bytes 随机数
         * 
         * 从定义可以看出，在同一秒内，在不同的机器上相同进程ID条件下，非常有可能生成相同的ObjectId。
         * 同时可以根据定义判断出，在给定条件下，ObjectId本身即可描述生成的时间顺序
         * ObjectId的存储使用Byte数组，而其展现需将Byte数组转换成字符串进行显示，所以通常我们看到的ObjectId都类似于：
         * ObjectId("507f191e810c19729de860ea")
         */
        private string _string;
        public ObjectId()
        {
        }
        public ObjectId(string value)
            : this(DecodeHex(value))
        {
        }
        internal ObjectId(byte[] value)
        {
            Value = value;
        }
        public static ObjectId Empty
        {
            get { return new ObjectId("000000000000000000000000"); }
        }
        public byte[] Value { get; private set; }
        public static ObjectId NewObjectId()
        {
            return new ObjectId { Value = ObjectIdHelper.Generate() };
        }
        public static bool TryParse(string value, out ObjectId objectId)
        {
            objectId = Empty;
            if (value == null || value.Length != 24)
            {
                return false;
            }
            try
            {
                objectId = new ObjectId(value);
                return true;
            }
            catch (FormatException)
            {
                return false;
            }
        }

        protected static byte[] DecodeHex(string value)
        {
            char[] _chars = value.ToCharArray();
            int _numberChars = _chars.Length;
            byte[] bytes = new byte[_numberChars / 2];
            for (int i = 0; i < _numberChars; i += 2)
            {
                bytes[i / 2] = Convert.ToByte(new string(_chars, i, 2), 16);
            }
            return bytes;
        }

        public override int GetHashCode()
        {
            return Value != null ? ToString().GetHashCode() : 0;
        }
        public override string ToString()
        {
            if (_string == null && Value != null)
            {
                _string = BitConverter.ToString(Value)
                  .Replace("-", string.Empty)
                  .ToLowerInvariant();
            }
            return _string;
        }
        public override bool Equals(object obj)
        {
            ObjectId _other = obj as ObjectId;
            return Equals(_other);
        }
        public bool Equals(ObjectId other)
        {
            return other != null && ToString() == other.ToString();
        }
        public static implicit operator string(ObjectId objectId)
        {
            return objectId == null ? null : objectId.ToString();
        }
        public static implicit operator ObjectId(string value)
        {
            return new ObjectId(value);
        }
        public static bool operator ==(ObjectId left, ObjectId right)
        {
            if (ReferenceEquals(left, right))
            {
                return true;
            }
            if (((object)left == null) || ((object)right == null))
            {
                return false;
            }
            return left.Equals(right);
        }
        public static bool operator !=(ObjectId left, ObjectId right)
        {
            return !(left == right);
        }
    }
}
